/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2025. All rights reserved.
 *
 */

import { hvigorCore, HvigorSystemPlugin, Module } from '@ohos/hvigor';
import fse from 'fs-extra';
import path from 'path';

import { DefaultTargetConst } from '../../const/common-const.js';
import { ModuleModel } from '../../model/module/module-model.js';
import { ProjectModel } from '../../model/project/project-model.js';
import { buildOptionManager } from '../../project/build-option/build-mode-manager.js';
import { BuildOptionUtil } from '../../project/build-option/build-option-util.js';
import { ApiType } from '../../project/data/hap-extra-info.js';
import { Clean } from '../../tasks/common/clean.js';
import { TaskNames } from '../../tasks/common/task-names.js';
import { ModuleTargetData } from '../../tasks/data/hap-task-target-data.js';
import { AbstractModuleHookTask } from '../../tasks/hook/abstract-module-hook-task.js';
import { CompileNativeHook } from '../../tasks/hook/native/compile-native-hook.js';
import { ModuleInspection } from '../../tasks/inspection/module-inspection.js';
import { ModuleTaskService } from '../../tasks/service/module-task-service.js';
import { ModuleTaskServiceFactory } from '../../tasks/service/module-task-service-factory.js';
import { TargetTaskService } from '../../tasks/service/target-task-service.js';
import { SyncModule } from '../../tasks/sync/sync-module.js';
import { OhosLogger } from '../../utils/log/ohos-logger.js';

import { OhosPluginId } from './ohos-plugin-id.js';
import SyncTask = TaskNames.SyncTask;
import { OhosModuleContextImpl } from '../context/impl/ohos-module-context-impl.js';
import { OhosModuleContext } from '../context/plugin-context.js';
import { moduleDependencyUtil } from '../../common/module-dependency-util.js';
import CommonHookTask = TaskNames.CommonHookTask;
import { ServiceLocator } from '../../arkts-evolution/service/service-locator.js';

const ohosTestTargetName = DefaultTargetConst.OHOS_TEST_TARGET;

/**
 * hap模块的抽象接口
 *
 * @since 2022/1/20
 */
export abstract class AbstractModulePlugin extends HvigorSystemPlugin {
  private _log: OhosLogger = OhosLogger.getLogger(AbstractModulePlugin.name);
  protected _module: Module;
  protected _projectModel: ProjectModel | undefined;
  protected _moduleModel: ModuleModel | undefined;
  protected _moduleService: ModuleTaskService | undefined;
  protected _configFileName: string | undefined;
  protected _needExecTargetServiceList: TargetTaskService[];
  protected _pluginApiType: string | undefined;
  protected ohosModuleContext: OhosModuleContext | undefined;
  protected readonly moduleName: string | undefined;

  public clean: Clean | undefined;
  public compileNative: CompileNativeHook | undefined;
  public sync: SyncModule | undefined;

  protected constructor(pluginId: string, module: Module) {
    super(pluginId);
    this._module = module;
    this.moduleName = this._module.getName();
    this._needExecTargetServiceList = [];
  }

  refreshTargetServiceList() {
    this._needExecTargetServiceList.forEach((targetService) => {
      targetService.refreshBuildOption();
    });
  }

  getContext(): OhosModuleContext {
    return this.ohosModuleContext!;
  }

  initContext(): void {
    this.ohosModuleContext = new OhosModuleContextImpl(this);
  }

  withProjectModel(projectModel: ProjectModel) {
    this._projectModel = projectModel;
    return this;
  }

  getProjectModel(): ProjectModel | undefined {
    return this._projectModel;
  }

  withModuleModel(moduleModel: ModuleModel) {
    this._moduleModel = moduleModel;
    return this;
  }

  getModuleModel(): ModuleModel | undefined {
    return this._moduleModel;
  }

  withPluginApiType(pluginApiType: ApiType) {
    this._pluginApiType = pluginApiType;
    return this;
  }

  withRuntimeConfigFileName(runtimeFileName: string) {
    this._configFileName = runtimeFileName;
    return this;
  }

  initModuleTaskService(isFaMode: boolean) {
    // 不能调整这行的顺序,需要先校验报错,再初始化_moduleService
    this.checkModuleConfigStatus();
    this._moduleService = ModuleTaskServiceFactory.initModuleTaskService(this._projectModel!, this._moduleModel!, isFaMode);

    // 注册moduleService
    ServiceLocator.registerModuleTaskService(this.moduleName!, this._moduleService);
  }

  initBuildOptionMap(path: string) {
    BuildOptionUtil.validateBuildOptionSet(this._moduleModel!);
    BuildOptionUtil.validateBinderSet(this._moduleModel!);
    buildOptionManager.initModuleBuildOptions(this._moduleModel!, path);
  }

  doModuleInspection() {
    const inspection: ModuleInspection = new ModuleInspection(this._moduleModel!);
    inspection.doInspection().exitOnError();
  }

  getNeedExecTargetServiceList(): TargetTaskService[] {
    return this._needExecTargetServiceList;
  }

  /**
   * 初始化获取当前模块有哪些target需要创建任务流，并挂接在hook任务上
   * 1. 如果当前模块所有target都与指定的product不匹配，则报一个warn
   *
   */
  initTargetDepends(): void {
    let hasTargetNeedPack = false;
    let targetProductName: string | undefined;

    for (const value of this._moduleService!.getTargetDataSet()) {
      const targetData: ModuleTargetData = value[0];
      targetProductName = targetData.getProduct().name;

      // 如果该target需要打包,则创建一系列任务流
      if (value[1]) {
        hasTargetNeedPack = true;
        const taskTargetService = new TargetTaskService(targetData, this._moduleService!);

        // 注册当前需要打包的TargetService
        ServiceLocator.registerTargetService(targetProductName, this.moduleName!, targetData.getTargetName(), taskTargetService);
        this._needExecTargetServiceList.push(taskTargetService);
        this._projectModel?.registryTarget(taskTargetService);
      }
    }

    const moduleModel = this._moduleService!.getModuleModel();
    const isHarModule = moduleModel.isHarModule();

    if (!isHarModule && !hasTargetNeedPack) {
      this._log.warn(
        `Current product is '${targetProductName}'. No output will be generated because of` + ` no executable target in module: '${this._module.getName()}.'`,
      );
    }
  }

  /**
   * 初始化 公共的与target无关的任务
   *
   * @private
   */
  initModuleCommonTasks() {
    this.clean = new Clean(this._module, this._moduleService!);
    this._module.registry(this.clean);

    this.compileNative = new CompileNativeHook(this._moduleService!);
    this._module.registry(this.compileNative);
  }

  initModuleCommonTasksForDiffApiType(isFaMode: boolean) {
    this.sync = new SyncModule(this._module!, SyncTask.syncTaskName(this._module.getName()), isFaMode);
    this._module.registry(this.sync);
  }

  protected initTaskDependsForAllNeedExecTarget(...tasks: AbstractModuleHookTask[]) {
    tasks.forEach((task) => {
      this._needExecTargetServiceList.forEach((targetTaskService) => {
        task.initTaskDepends(targetTaskService);
      });
    });
  }

  /**
   * 校验Module Target配置
   */
  private checkModuleTarget() {
    // check hvigor命令行中配置的target （命令运行时、指定了target时会校验）
    this.checkHvigorCommandTarget();

    // check target对应的源码目录及配置文件 （基础校验）
    this.checkTargetSourceCodeConfigFile();
  }

  /**
   * 检查hvigor命令行中配置的target
   */
  checkHvigorCommandTarget(): void {
    // 命令行参数中配置的target
    const curCommandModuleTargets = this._projectModel!.getModuleSpecificTargets().get(this._moduleModel!.getName());
    if (curCommandModuleTargets) {
      curCommandModuleTargets
        .filter((targetName) => targetName !== 'all')
        .forEach((targetName) => {
          const isExists = this._moduleModel!.getTargetOptions().filter((target) => target.name === targetName).length > 0;

          // 校验命令参数中的target是否存在
          if (!isExists) {
            this._log.printErrorExit(
              'TARGET_NOT_EXIST_IN_COMMAND',
              [targetName, this._module.getName()]
            );
          }

          if (targetName === ohosTestTargetName) {
            this.checkOhosTestTarget();
          }
        });
    }
  }

  /**
   * 校验已配置、或默认的target源码目录下的配置文件
   */
  private checkTargetSourceCodeConfigFile(): void {
    this._moduleModel!.getTargetOptions().forEach((target) => {
      const targetName = target.name;
      const sourceRoot = this._moduleModel!.getSourceRootByTargetName(targetName);
      const configFilePath = path.resolve(sourceRoot, this._configFileName!);

      if (targetName === ohosTestTargetName) {
        return;
      }

      // 校验源码目录和配置文件必须存在
      if (fse.existsSync(configFilePath)) {
        return;
      }

      const cause = `The configuration file '${configFilePath}' for the target ` + `'${targetName}' is not found in the '${this.moduleName}' module.`;
      const solution =
        `Ensure that the '${path.relative(this.moduleName!, sourceRoot)}' ` +
        'directory has been created for the module ' +
        `and the '${this._configFileName}' file exists in the directory.`;

      this._log._buildError(cause)._solution(solution)._printErrorAndExit();
    });
  }

  /**
   * 校验module文件配置与module工程模型是否有差异
   *
   * @private
   */
  checkModuleConfigStatus() {
    const isHapModule = this.pluginId === OhosPluginId.OHOS_HAP_PLUGIN;

    // build-profile.json5中的apiType根hvigorfile.js中的引用的插件状态不一致
    const apiType = this._pluginApiType;
    if (this._moduleModel === undefined || this._moduleModel.getApiType() !== apiType) {
      this._log.printErrorExit(
        'MISMATCH_API_TYPE',
        [this._moduleModel ? this._moduleModel.getName() : 'project'],
        [[apiType, this._module.getBuildProfilePath()]]
      );
    }

    // 先校验apiType， 再校验配置文件target
    this.checkModuleTarget();

    // hvigorfile.js中引用的插件跟模块的真实状态不一致,比如hap模块引用了harPlugin
    if (this._moduleModel!.isHapModule() !== isHapModule) {
      this._log.printErrorExit(
        'PLUGIN_DOES_NOT_MATCH_MODULE',
        [],
        [[this._moduleModel!.getName(), `${this._module.getBuildFilePath()}.ts`]]
      );
    }
  }

  /**
   * ohosTest Target规格校验
   */
  checkOhosTestTarget(): void {
    const isHarModule = this._moduleModel!.isHarModule();
    const isHspModule = this._moduleModel!.isHspModule();
    const mutexTaskName = isHarModule || isHspModule ? (isHarModule ? CommonHookTask.ASSEMBLE_HAR.name : CommonHookTask.ASSEMBLE_HSP.name) : '';
    if (mutexTaskName && hvigorCore.isCommandEntryTask(mutexTaskName)) {

      this._log.printErrorExit(
        'OHOSTEST_NOT_SUPPORT_HAR_HSP',
        [mutexTaskName, ohosTestTargetName, isHarModule ? 'HAR' : 'HSP'],
        [[],[],[hvigorCore.isCommandEntryTask(CommonHookTask.GEN_ON_DEVICE_TEST_HAP.name)
          ? `Remove it and run '${CommonHookTask.GEN_ON_DEVICE_TEST_HAP.name}' alone.`
          : `Run '${CommonHookTask.GEN_ON_DEVICE_TEST_HAP.name}' instead.`]]
      );
    }
    const sourceRoot = this._moduleModel!.getSourceRootByTargetName(ohosTestTargetName);

    // 检查配置文件src/ohosTest/config.json(module.json5)是否存在
    if (fse.existsSync(path.resolve(sourceRoot, this._configFileName!))) {
      return;
    }

    this._log.printErrorExit(
      'OHOSTEST_CONFIG_PROFILE_NOT_FOUND',
      [this.moduleName, this.moduleName, this._configFileName],
      [[this.moduleName,this.moduleName, this._configFileName!, this._moduleModel!.getProfilePath()]]
    );
  }

  public getTaskService(): ModuleTaskService | undefined {
    return this._moduleService;
  }
}
